apt 中的keyrings(gpg)

·

apt gpg 作用

在 APT 的工作流程中,GPG 签名验证(signed-by= 指定的公钥)是在 “获取 Release 文件并校验其完整性与真实性” 阶段起作用的,具体发生在 apt update 的早期阶段,早于包列表下载和安装决策。

  • 解析 sources.list ,识别源 URL 和组件 ( bookworm , stable)
  • 下载 InRelease 或 Release + Release.gpg 文件: → https://download.docker.com/linux/debian/dists/bookworm/InRelease 或 → .../Release + .../Release.gpg 关键阶段
  • 用 /etc/apt/keyrings/docker.asc 中的公钥验证 InRelease/Release.gpg 签名 ️ GPG 认证生效点
  • 若验证失败 → 立即报错退出 (如 NO_PUBKEY , GPG error , signature invalid )
  • 若验证成功 → 解析 Release 文件中的 SHA256 / SHA512 校验和 (哈希校验,非 GPG)
  • 下载 Packages.gz 等索引文件,并用 Release 中的哈希值校验完整性
  • 建立本地包数据库( /var/lib/apt/lists/... )
  • apt install 时仅校验 .deb 包哈希(来自 Packages ), 不再 GPG 验证
# 1. 故意破坏签名验证(删掉 keyring)
sudo rm /etc/apt/keyrings/docker.asc
sudo apt update 2>&1 | grep -A2 -B2 "GPG\|NO_PUBKEY"
# → 报错:The following signatures couldn't be verified...
# 2. 或临时禁用验证(危险!仅测试)
sudo apt -o Acquire::Check-Valid-Until=false \
         -o Acquire::AllowInsecureRepositories=true \
         -o Acquire::AllowDowngradeToInsecureRepositories=true \
         update

# → 仍可能失败,因为 signed-by 指定了强制 key,除非改 sources.list 去掉 signed-by

使用镜像源(mirror)的 APT 服务,还能正常 GPG 认证吗?

  • 可以,只要镜像同步了完整的 dists/ 目录(含 InRelease / Release + Release.gpg)——绝大多数合规镜像都做到了。
  • APT 的 GPG 校验不依赖源服务器域名/IP,只校验: 文件内容是否被篡改(哈希匹配) Release 文件是否由可信私钥签名(GPG 签名校验)

验证实操(推荐)

# 1. 下载 InRelease(中科大镜像)
wget https://mirrors.ustc.edu.cn/docker-ce/linux/debian/dists/bookworm/InRelease

# 2. 用你本地的 docker.asc 公钥验证
gpg --keyring /etc/apt/keyrings/docker.asc \
    --verify InRelease
# 应输出:Good signature from "Docker Release (CE deb) ..."

# 3. 对比原始源(应完全一致)
diff InRelease \
  <(curl -s https://download.docker.com/linux/debian/dists/bookworm/InRelease)
# → 无输出(完全相同)

报错排查

gpg: [don't know]: invalid packet (ctb=2d)
gpg: keydb_search failed: 无效的数据包
gpg: 无法检查签名:缺少公钥
  • 诊断依据 你从 https://download.docker.com/linux/debian/gpg 下载的 gpg 文件(3.7KB)是 ASCII-armored 公钥
  • 但 --keyring /path/to/file 默认期望的是 二进制格式的 keyring(或 GnuPG native keybox)
  • 当你直接把 ASCII-armored .asc 传给 --keyring,GPG 会把它当二进制解析 → 遇到 -----BEGIN 这种文本头 → 解析为“无效数据包”(ctb=2d 是 0x2d = '-' 字符)→ 失败 ❌

先 dearmor 成二进制,再验证(推荐)

# 1. 确保 docker.asc 是 ASCII-armored(检查开头)
head -n2 /etc/apt/keyrings/docker.asc
# 应输出:-----BEGIN PGP PUBLIC KEY BLOCK-----
# 2. 转成二进制格式(例如 docker.gpg)
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg /etc/apt/keyrings/docker.asc
# 3. 用 .gpg(二进制)验证
gpg --keyring /etc/apt/keyrings/docker.gpg \
    --verify ./InRelease
# ✅ 此时应显示:Good signature from "Docker Release (CE deb)..."

不指定 --keyring,而是先导入公钥到临时 keyring

# 创建临时 home
mkdir -p /tmp/gpghome && chmod 700 /tmp/gpghome
# 导入公钥(自动 dearmor)
gpg --homedir /tmp/gpghome --import /etc/apt/keyrings/docker.asc
# 用临时 keyring 验证
gpg --homedir /tmp/gpghome --verify ./InRelease
# 清理
rm -rf /tmp/gpghome

为什么 apt 本身能工作?

因为 APT 内部 自动处理 dearmor! 当你在 sources.list 写:

deb [signed-by=/etc/apt/keyrings/docker.asc] ...

APT 会: 检测 .asc 后缀或文件内容含 -----BEGIN → 自动 dearmor 后加载

不依赖 gpg CLI 行为 → 所以 apt update 正常,但你手动用 gpg --keyring 却失败。

# 查看二进制 keyring 内容
gpg --keyring /etc/apt/keyrings/docker.gpg --list-packets </dev/null
# 应显示 pub, sub 等 key packets,无 "invalid packet"

# 或直接看指纹(对比官网)
gpg --keyring /etc/apt/keyrings/docker.gpg --with-fingerprint --list-keys
# 应含:7EA0 A9C3 F273 FCD8 (即你日志中的 RSA 密钥 ID)

总结:

  • .asc 是文本格式,不能直接作 --keyring;必须 --dearmor 转 .gpg 二进制。
  • APT 能自动处理,但 gpg CLI 不会 —— 这是常见坑点